#!/usr/bin/python

import sys, os, random, time
import struct
import serial
import time
from datetime import datetime

RELLENO=21       # 21  = 0x15 -> Relleno de 6 bits bajos de conversion
MARCA=170        # 170 = 0xAA -> Marca de final de serie

NUMCANALES=25    # Numero de canales que lee el medidor
MAXCANALES=25    # Numero de canales disponibles
TAMDATA=2        # Numero de bits con los que se muestran los datos
TIME_MASK=6      # Mascara de bits que define los bytes usados para definir el tiempo
TAMTIME=2        # Numero de bytes con los que se muestra el time

FICH_SALIDA = "datos.txt"     #Fichero de salida de datos


fout = 0 
ser = 0
ant = 0

def EsRelleno(byte):
    return (byte & RELLENO == RELLENO)

def EsMarca(byte):
    return byte == MARCA

def analizarTime(primero):
    global ant

    times = [0 for i in range (TAMTIME)]
    for i in range (TAMTIME):
        strdato=ser.read(1)
        time = int(hex(ord(strdato)).replace('0x', ''), 16)
        times[i]=time

    bits = [8,4,2,1]
    resul=0
    c=0
    for i in range (4):
        if TIME_MASK & bits[i] == bits[i]:
            resul += times[c]
            c+=1
        if(i!=3):
            resul = resul * 256

    resul *=4

    str_resul = str(resul)

    if primero:
#        print "time = " + str(resul)
#        print "diff time: " + str(resul) + " - " + str(ant) + " = " + str(resul - ant)
#        ant = resul

        fout.write("time= ")
        fout.write(str_resul)

#        dt = datetime.now()
#        fout.write("\tPython time= ")
#        fout.write(str(dt.microsecond))

        fout.write("\n");

def interpretaEntrada(cadena, numdatos, tema):
    lista=cadena.split(",")
    sel = [0 for i in range (numdatos)]

    for op in lista:
        if not "-" in op:
            if int(op) < numdatos:
                sel[int(op)]=1
            else:
                if tema=="configuration":
                    print "ERROR, the channels selected do not exist. Identification of channels (0:" + str(numdatos-1) + ")"
                elif tema == "time":
                    print "ERROR, the time bytes selected do not exist. Identification of time bytes (0:" + str(numdatos-1) + ")"
                return -1

        else:
            rangos=op.split("-")
            int_ran0 = int (rangos[0])
            int_ran1 = int (rangos[1])
            if int_ran0 < numdatos and int_ran1 < numdatos and len(rangos)==2: 
                for dato in range (int_ran0, int_ran1 + 1):
                    sel[dato]=1
            else:
                if tema=="configuration":
                    print "ERROR, configuration bytes ill-defined. Identification of channels (0:" + str(numdatos-1) + ")"
                if tema=="time":
                    print "ERROR, time bytes ill-defined. Identification of time bytes (0:" + str(numdatos-1) + ")"        
                return -1

    return sel

def interpretaBinario(mask, num):
    result = 0
    aux = 1
    for i in range(num):
        result += mask[i] * aux
        aux *= 2
    return result

def contadorCanales(cmask):
    total = 0
    for i in range (25):
        if cmask[i]==1:
            total += 1
    return total

def contadorBytesTime(tmask):
    total = 0
    for i in range (4):
        if tmask[i]==1:
            total += 1
    return total


def anlizaConfiguracion(datos):
    if (datos[0] & 128 == 128):
        print "10 bits data"
    else:
        print "8 bits data"

    if (datos[0] & 64 == 64):
        print "byte 3 of time enable"
    else:
        print "byte 3 of time off"

    if (datos[0] & 32 == 32):
        print "byte 2 of time enable"
    else:
        print "byte 2 of time off"

    if (datos[0] & 16 == 16):
        print "byte 1 of time enable"
    else:
        print "byte 1 of time off"

    if (datos[0] & 8 == 8):
        print "byte 0 of time enable"
    else:
        print "byte 0 of time off"

    print "\nChannel\tnum\tstate\t\tenable\toff"
    bits = [1,128,64,32,16,8,4,2,1,128,64,32,16,8,4,2,1,128,64,32,16,8,4,2,1]

    for i in range (25):
        if (datos[3-(24-i)/8] & bits[i] == bits[i]):
            print "Channel\t" + str(24-i) + "\tenable\t\t" + str(24-i)
        else:
            print "Channel\t" + str(24-i) + "\toff\t\t\t" + str(24-i)





def main(fichero):
    global TAMDATA
    global TIME_MASK
    global TAMTIME
    global NUMCANALES
    global ser

    try:
        ser = serial.Serial(fichero, 115200, parity=serial.PARITY_NONE, timeout = 1)
    except:
        print "Error, medidor de potencia mal conectado. Asegurarse que se ha definido correctamente el nombre del puerto serie."
        exit (-1)


    print "Configuration commands (EXIT to finalize)\n>COMMAND OPTIONS"

    cmd=""
    modo="conversacion"
    while cmd not in ["exit", "EXIT", "e", "E"]:
        cmd = raw_input(">")

        cmd_vec = cmd.split()
        
        if len(cmd_vec) > 2:
            print "Only one argument to define the options, do not use whitespaces inside options"
            continue

        if cmd_vec[0] in ["MODECONV", "modeconv", "mc", "MC"]:
            ser.write("\x01")
            ser.flush()
            ser.flushInput()
            ser.write("\x55")
            modo="conversacion"
            ser.read(1)
            ser.flush()
            ser.flushInput()
#Ignorar primer ciclo
            ser.read(NUMCANALES*TAMDATA+TAMTIME+2)

        elif cmd in ["orden", "ORDEN", "o", "O"]:
            ser.write("\x01")
            modo="configuracion"
            ser.flush()

        elif cmd in ["bucle"]:
            ant = 0
            annn = open('out.bin', 'w')
            for i in range (1000):
                linea = 0
                for j in range (56):
                    strdato=ser.read(1)
                    dt = datetime.now()
                    linea += (dt.microsecond - ant)
                    print "byte " + str(dt.microsecond - ant)
                    ant = dt.microsecond
                   
                    annn.write(strdato)
                   
                print "linea " + str(linea)
            annn.close()



        elif cmd in ["BITS8", "bits8", "b8", "B8"]:
            if modo == "conversacion":
                modo="configuracion" 
                ser.write("\x01")

            ser.flush()
            ser.flushInput()

            ser.write("\x51")
            TAMDATA=1

        elif cmd in ["BITS10", "bits10", "b10", "B10"]:
            if modo == "conversacion":
                modo="configuracion"
                ser.write("\x01")

            ser.flush()
            ser.flushInput()

            ser.write("\xA1")
            TAMDATA=2

        elif cmd_vec[0] in ["SETTIME", "settime", "st", "ST"]:
            if modo == "conversacion":
                modo="configuracion"
                ser.write("\x01")

            ser.flush()
            ser.flushInput()

            ser.write("\xA9")
              
            if len(cmd_vec) != 2:
                print "ERROR, missing options"
            else:
                tmask = interpretaEntrada(cmd_vec[1], 4, "time")
                if tmask==-1:
                    continue
                int_tmask = interpretaBinario(tmask, 4)
                data = struct.pack('b', int_tmask)    
                ser.write(data)
                TIME_MASK=int_tmask
                TAMTIME=contadorBytesTime(tmask)

        elif cmd in ["GETTIME", "gettime", "gt", "GT"]:
            if modo == "conversacion":
                modo="configuracion"
                ser.write("\x01")

                ser.flush()
                ser.flushInput()

#Algunas veces en la primera peticion de configuracion salen datos incorrectos
                ser.write("\x59")
                strdato = ser.read(1)

            ser.flush()
            ser.flushInput()

            ser.write("\x59")
            total = 0
            total_mask = 0
            binary = [1,2,4,8]
#si se lee otro dato es la marca que indica que todo es correcto
            for i in [3,2,1,0]:
                time=ser.read(1)
                dato = int(hex(ord(time)).replace('0x', ''), 16)
                print "time", str(i), ": ", str(dato)

                total = total + dato

                if (binary[i] & TIME_MASK == binary[i]):
                    total_mask = total_mask + dato

                if (i!=0):
                    total *= 256
                    total_mask *= 256

            print "total: " + str(total*4)
            print "total mask: " + str(total_mask*4)
                
        elif cmd_vec[0] in ["SETCHANNEL", "setchannel", "sc", "SC"]:
            if modo == "conversacion":
                modo="configuracion"
                ser.write("\x01")

                ser.flush()
                ser.flushInput()

            ser.write("\xAA")

            if len(cmd_vec) != 2:
                print "ERROR, missing options"
            else:            
                cmask = interpretaEntrada(cmd_vec[1], MAXCANALES, "configuration")
                if cmask==-1:
                    continue
                
                for i in range (3,-1,-1):
                    if i==3:
                        int_cmask = cmask[24]
                    else:
                        int_cmask = interpretaBinario(cmask[(i*8):((i+1)*8)], 8)

                    if int_cmask>127:
                        int_cmask=-(256-int_cmask)

                    data = struct.pack('b', int_cmask)
                    ser.write(data)

                NUMCANALES = contadorCanales(cmask)
            

        elif cmd_vec[0] in ["GETCHANNEL", "getchannel", "gch", "GCH"]:
            if modo == "conversacion":
                modo="configuracion"

                ser.write("\x01")

                ser.flush()
                ser.flushInput()

#Algunas veces en la primera peticion de configuracion salen datos incorrectos
                ser.write("\x50")
                ser.write("\x01")
                strdato = ser.read(1)

            ser.flush()
            ser.flushInput()

            ser.write("\x50")

            if len(cmd_vec)<2:
                channel = -1
                while (channel < 0 or channel > 24):
                    str_channel = raw_input("Channel to read (integer from 0 to 24):")
                    channel = int(str_channel)
            
            else:
                channel = int(cmd_vec[1])

            data = struct.pack('b', channel)
            ser.write(data)

            if(TAMDATA==2):
                strdato = ser.read(1)
                byte1 = int(hex(ord(strdato)).replace('0x', ''), 16)
                strdato = ser.read(1)
                byte2 = int(hex(ord(strdato)).replace('0x', ''), 16)
                dato = byte1 * 4 + (byte2 - RELLENO) / 64
            else:
                strdato = ser.read(1)
                byte1 = int(hex(ord(strdato)).replace('0x', ''), 16)
                dato = byte1 * 4

            dato = float (dato)
            print "Intensidad= " + str(dato * 0.02) + " A"


        elif cmd in ["GETCONFIG", "getconfig", "gcon", "GCON"]:
            if modo == "conversacion":
                modo="configuracion"
                ser.write("\x01")

                ser.flush()
                ser.flushInput()

#Algunas veces en la primera peticion de configuracion salen datos incorrectos
                ser.write("\x5A")
                strdato = ser.read(1)


            ser.flush()
            ser.flushInput()

            ser.write("\x5A")

            datos = [0,0,0,0]
                
            for i in range (4):
                strdato = ser.read(1)
                dato = int(hex(ord(strdato)).replace('0x', ''), 16)
                datos[i]=dato
#            print str(datos[0]) + " " + str(datos[1]) + " " + str(datos[2]) + " " + str(datos[3]) 
            anlizaConfiguracion(datos)
                
            
        elif cmd_vec[0] in ["exit", "EXIT", "e", "E"]:
            if modo == "configuracion":
                option = raw_input("Configuration mode active, do you wish exit anyway?(y/N)")
                if option not in ["y", "Y"]:
                    print "Returning to configuration commands (EXIT to finalize)"
                    cmd=""
        else:
            print "Incorrect command"

    print "Configuration finished"

    global fout
    fout=open(FICH_SALIDA, 'w')


#Empezamos a leer datos
    strdato=ser.read(1)

    ant_marca = False;
    encontrado = False;

#Buscar dos marcas consecutivas
    while strdato!='' and not (encontrado):
        dato = int(hex(ord(strdato)).replace('0x', ''), 16)

        if EsMarca(dato):
            print "es marca"
            if ant_marca:
                encontrado = True
            else:
                ant_marca = True
        else:
#            print "no es marca"
            ant_marca = False
        
        strdato=ser.read(1)

#Asegurarse de que los datos se corresponden con la configurcion
    cont=0
    while (cont!=NUMCANALES*TAMDATA+TAMTIME+2):
        print str(NUMCANALES) + "*" + str(TAMDATA) + "+2+" + str(TAMTIME)
        encontrado = False
        cont = 0
        while strdato!='' and not (encontrado):
            cont +=1
            dato = int(hex(ord(strdato)).replace('0x', ''), 16)

            if EsMarca(dato):
                if ant_marca:
                    encontrado = True
                    print cont
                else:
                    ant_marca = True
            else:
                ant_marca = False
        
            strdato=ser.read(1)


#Recorrer toda una traza de datos para comprobar que el formato se ajusta con lo que espera el usuario    
    for i in range (NUMCANALES*TAMDATA):
      dato = int(hex(ord(strdato)).replace('0x', ''), 16)
      if (TAMDATA==2):
          if i % 2 == 1:
              if not EsRelleno(dato):
                  print "ERROR, the format of the output data is not correct. Check the number of lines"
                  exit(-1)
          
      strdato=ser.read(1)

#Saltarnos el time y las marcas
    analizarTime(False)      
    strdato=ser.read(1)
    strdato=ser.read(1)


#Leer y escribir en el nuevo fichero
    cont=0;
    iterations = 5000;
    while strdato!='' and iterations>0:
        if (TAMDATA==2):
            byte1 = int(hex(ord(strdato)).replace('0x', ''), 16)
            strdato = ser.read(1)
            byte2 = int(hex(ord(strdato)).replace('0x', ''), 16)
            dato = byte1 * 4 + (byte2 - RELLENO) / 64
            dato = float (dato)
            cont+=2
        else:
            dato = int(hex(ord(strdato)).replace('0x', ''), 16)
            dato = dato * 4
            dato = float (dato)
            cont+=1

        fout.write(str(dato * 0.02) + "\t")
        iterations-=1

        if cont == (NUMCANALES * TAMDATA):
            #Saltarnos el time y las marcas
            cont = 0
            analizarTime(True)

            #marcas
            ant_marca = False;
            encontrado = True;

            strdato=ser.read(1)
            dato = int(hex(ord(strdato)).replace('0x', ''), 16)
            if not(EsMarca(dato)):
                print "ERROR, no es marca1"
                print "En el dato: " + str((5000 - iterations)/25)
                encontrado = False

            strdato=ser.read(1)
            dato = int(hex(ord(strdato)).replace('0x', ''), 16)
            if not(EsMarca(dato)):
                print "ERROR, no es marca2"
                encontrado = False
            else:
                ant_marca = True


            #Buscar dos marcas consecutivas
            while strdato!='' and not (encontrado):
                strdato=ser.read(1)
                dato = int(hex(ord(strdato)).replace('0x', ''), 16)

                if EsMarca(dato):
                    print "es marca"
                    if ant_marca:
                        encontrado = True
                    else:
                        ant_marca = True
                else:
                    print "."
                    ant_marca = False
        
        strdato = ser.read(1)

    ser.close()
    fout.close()
    return 0
 
if __name__ == '__main__':
    if len(sys.argv)<2:
        print "Use: ordenes <nombre_entrada>"
    else:
        main(sys.argv[1])
        
